#include "TwApplicationPCH.h"
#include "TwRibbonTrail.h"

TwRibbonTrail::TwRibbonTrail()
: mTrailWidth(1.0f)
, mTrailLength(0.0f)
, mTwemLength(0.0f)
, mSquaredTwemLength(0.0f)
, mTraceDirty(true)
{

}

TwRibbonTrail::~TwRibbonTrail()
{

}

void TwRibbonTrail::setTrailLength(float len)
{
	mTrailLength = len;
	mTwemLength = mTrailLength / mMaxTwements;
	mSquaredTwemLength = mTwemLength * mTwemLength;
}

void TwRibbonTrail::setTrailWidth(float width)
{
	clearChainTwements();
	mTrailWidth = width;
	mTraceDirty = true;
}

void TwRibbonTrail::setMaxChainTwements(size_t maxTwements)
{
	TwBillboardChain::setMaxChainTwements(maxTwements);
	mTwemLength = mTrailLength / mMaxTwements;
	mSquaredTwemLength = mTwemLength * mTwemLength;

	resetTrail();
}

void TwRibbonTrail::updateTrail(const D3DXVECTOR3& position)
{
	bool update = false;
	if (mTraceList.empty())
		update = true;
	else
	{
		D3DXVECTOR3 diff = mTraceList[0] - position;
		if (D3DXVec3LengthSq(&diff) >= mSquaredTwemLength)
			update = true;
		else
			update = false;
	}

	if (update)
	{
		while (mTraceList.size() >= mMaxTwements)
			mTraceList.erase(mTraceList.begin());
		mTraceList.push_back(position);
		mTraceDirty = true;
	}
}

void TwRibbonTrail::resetTrail()
{
	mTraceList.clear();
	mTraceDirty = true;
}

void TwRibbonTrail::update()
{
	_updateChainTwements();
	TwBillboardChain::update();
}

float TwRibbonTrail::getSquaredViewDepth(TwCamera* cam) const
{
	// we only need this for transparent objects
	float dist = 0.0f;
	if (!mTraceList.empty())
		dist = D3DXVec3LengthSq(&(mTraceList[0] - cam->getRealPosition()));
	return dist;
}

void TwRibbonTrail::attachNode(TwSceneNode* node)
{
	if (node->getListener())
	{
		assert(0 && std::string(mName + " cannot monitor node " + node->getName() + " since it already has a listener. RibbonTrail::addNode().").c_str());
	}

	// initialise the chain
	resetTrail();

	node->setListener(this);
}

void TwRibbonTrail::nodeUpdated(const TwSceneNode* node)
{
	updateTrail(node->getDerivedPosition());
}

void TwRibbonTrail::nodeDestroyed(const TwSceneNode* node)
{
	const_cast<TwSceneNode*>(node)->setListener(NULL);
}

void TwRibbonTrail::_createChainTwements()
{
	clearChainTwements();

	float texCoordScale = 1.0f / (mMaxTwements - 1);
	for (size_t i = 0; i < mMaxTwements; ++i)
	{
		Twement e;
		e.width = mTrailWidth;
		e.texCoord = 1.0f - texCoordScale * (float)i;
		addChainTwement(e);
	}
}

void TwRibbonTrail::_updateChainTwements()
{
	if (mTraceDirty)
	{
		if (!mTraceList.empty())
		{
			if (mChainTwementList.size() != mMaxTwements)
				_createChainTwements();

			int numTraces = (int)mTraceList.size() - 1;
			for (int i = 0; i < mMaxTwements; ++i)
			{
				int idx = max(0, numTraces - i);
				mChainTwementList[i].position = mTraceList[idx];
			}

			needUpdate();
		}

		mTraceDirty = false;
	}
}